package cn.piggy.gateway.core.client;

import cn.piggy.gateway.core.common.CommonUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.AttributeKey;
import java.util.HashMap;
import java.util.Map;

public class NettyClient {

    private static final Map<String, Map<String,Channel>> channels = new HashMap<>();

    public synchronized static void remove(String application, String address){
        Map<String, Channel> stringChannelMap = channels.get(application);
        Channel channel = stringChannelMap.remove(address);
        channel.close();
    }

    public synchronized static void init(String application, String address){
        try {
            Map<String, Channel> map = channels.get(application);
            if(map==null){
                map = new HashMap<>();
                Channel channel = getChannel(application, address);
                map.put(address,channel);
                channels.put(application,map);
            }else{
                Channel channel = map.get(address);
                if(channel==null){
                    channel = getChannel(application, address);
                    map.put(address,channel);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static Channel getChannel(String application, String address) throws InterruptedException {
        String[] split = address.split(":");
        ChannelFuture future = getBootstrap().connect(split[0], Integer.parseInt(split[1])).sync();
        Channel channel = future.channel();
        channel.attr(AttributeKey.valueOf("application")).set(application);
        channel.attr(AttributeKey.valueOf("address")).set(address);
        return channel;
    }

    public static Bootstrap getBootstrap(){
        Bootstrap bootstrap = new Bootstrap();
        EventLoopGroup group = new NioEventLoopGroup(1);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.group(group);
        bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) {
                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                ch.pipeline().addLast(new StringDecoder());
                ch.pipeline().addLast(new PiggyClientHandler());
                ch.pipeline().addLast(new LengthFieldPrepender(4, false));
                ch.pipeline().addLast(new StringEncoder());
            }
        });
        return bootstrap;
    }

    public static Object sendRequest(String application, String address, Request request) {
        PropertyFilter filter = (object, name, value) -> !name.equals("latch");
        String requestStr = JSONObject.toJSONString(request,filter);
        Map<String, Channel> stringChannelMap = channels.get(application);
        Channel channel = stringChannelMap.get(address);
        ChannelFuture channelFuture = channel.writeAndFlush(requestStr);
        channelFuture.addListener((ChannelFutureListener) future -> {
            if(!future.isSuccess()){
                CommonUtil.futures.remove(request.getId());
            }
        });
        return request.waitResult(request);
    }


}


